package autoz.czt;

import static autoz.czt.launching.LaunchConfigurationAttributes.MODEL;
import static autoz.czt.launching.LaunchConfigurationAttributes.OUTPUT_FILE_PATH;
import static autoz.czt.launching.LaunchConfigurationAttributes.SOURCE;
import static autoz.czt.launching.LaunchConfigurationAttributes.USE_DEFAULT_TEMPLATES;

import java.io.File;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.FileLocator;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Platform;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.epsilon.common.dt.launching.extensions.ModelTypeExtension;
import org.eclipse.epsilon.common.dt.util.EclipseUtil;
import org.eclipse.epsilon.commons.util.StringProperties;
import org.eclipse.epsilon.eol.EolEvaluator;
import org.eclipse.epsilon.eol.models.IModel;
import org.eclipse.uml2.uml.Association;
import org.eclipse.uml2.uml.Class;

import autoz.core.AbstractAutozModule;
import autoz.core.AutozCorePlugin;
import autoz.core.problems.UmlProblem;
import autoz.czt.launching.ZOOSchemaNames;
import autoz.czt.uml.UmlAssociation;
import autoz.czt.uml.UmlClass;
import autoz.czt.uml.UmlDiagram;

public class CZTModule extends AbstractAutozModule {

	public String mode;

	public CZTModule(ILaunchConfiguration configuration) {
		super(configuration);
	}

	@Override
	protected List<IModel> extractModelsFromConfig() {
		// Adapted from
		// org.eclipse.epsilon.eol.dt.launching.EclipseContextManage.java
		List<IModel> models = new ArrayList<IModel>();

		try {
			
			for (Object s : configuration.getAttribute(MODEL, new ArrayList<String>())){
				IModel model = loadModelFromPropertiesString(s.toString());
				models.add(model);
			}
			
		} catch (CoreException e) {
			// TODO
			e.printStackTrace();
			return null;
		}
		
		return models;
	}

	private IModel loadModelFromPropertiesString(String props) {
		StringProperties properties = new StringProperties();
		properties.load(props);

		IModel model = null;

		try {
			model = ModelTypeExtension.forType(properties.getProperty("type"))
					.createModel();
			model.load(properties, EclipseUtil.getWorkspacePath());
		} catch (Exception e) {
			e.printStackTrace();
		}

		return model;
	}

	/**
	 * Gets the source EGL file to run - if the run configuration states to use
	 * the default templates then they are accessed from the bundle. Otherwise,
	 * use the specified templates.
	 */
	@SuppressWarnings("deprecation")
	@Override
	public String getLogicFileLocation()  {
		String workspaceLocation = ResourcesPlugin.getWorkspace().getRoot()
				.getRawLocation().toPortableString();
		try {
			if (configuration.getAttribute(USE_DEFAULT_TEMPLATES, true)) {
				// Need to grab the templates from the bundle
				URL defSourceFile = FileLocator.find(AutozCorePlugin.getDefault()
						.getBundle(), new Path(DEFAULT_SOURCE_FILE), null);
				
				URL realUrl = Platform.resolve(defSourceFile);
				return realUrl.getPath();
			} else {
				return workspaceLocation + configuration.getAttribute(SOURCE, "");
			}
		} catch (Exception e) {
			AutozCorePlugin.logMessage(IStatus.ERROR, e.getMessage(), e, true);
		}
		
		System.out.println("FLAPPPS!");
		return "";
	}

	protected File getTargetFile(){
		String workspaceLocation = ResourcesPlugin.getWorkspace().getRoot()
		.getRawLocation().toPortableString();
		String outputFilePath = "";
		try {
			outputFilePath = workspaceLocation
				+ configuration.getAttribute(OUTPUT_FILE_PATH, "");
		} catch (CoreException e) {
			AutozCorePlugin.logMessage(IStatus.ERROR, e.getMessage(), e, true);
		}
		
		return new File(outputFilePath);
	}
	
/**
 * TODO Need to handle the case where there is more than one diagram.
 * @param umlDiagram
 * @param typecheckErrors
 * @return
 * @throws CoreException
 */
	public List<UmlProblem> assignTypecheckErrorsToModelElements(UmlDiagram umlDiagram, List<UmlProblem> typecheckErrors) throws CoreException {
		for (UmlProblem typecheckProblem : typecheckErrors) {
			String schemaName = typecheckProblem.getSchemaLocation();
			boolean foundSchemaName = false;

			// Determine whether the schema is related to a particular class
			for (UmlClass c : umlDiagram.getClasses()) {
				if (schemaName.contains(c.getName())) {
					foundSchemaName = true;
					// ...
					String umlLocationText = c.getName();
					for (String op : c.getOperations()) { // Check whether it's an operation schema
						if (schemaName.replace(c.getName(),"").contains(op)){
							umlLocationText += ":" + op;
						}
					}
					
					typecheckProblem.setUMLLocation(umlLocationText);
					typecheckProblem.setMatched(true);
					break;
				}
			}

			// If it wasn't one of the class names, try association
			if (!foundSchemaName) {
				for (UmlAssociation a : umlDiagram.getAssociations()) {
					if (schemaName.contains(a.getName())) {
						foundSchemaName = true;
						// ...
						typecheckProblem.setUMLLocation(a.getName());
						typecheckProblem.setMatched(true);
						break;
					}
				}

				// Alternatively, see if it's one of the specials
				if (!foundSchemaName) {
					for (String special : ZOOSchemaNames.getSpecialSchemaNames()) {
						if (schemaName.contains(special)) {
							foundSchemaName = true;
							// ...
							typecheckProblem.setUMLLocation(special);
							typecheckProblem.setMatched(true);
							break;
						}
					}

					// Finally, if we've not found it, set the uml location
					// to be the schema location, plus a tag
					if (!foundSchemaName) {
						typecheckProblem.setUMLLocation(typecheckProblem.getSchemaLocation() + ":#");
					}
				}
			}
		}
		return typecheckErrors;
	}

	@SuppressWarnings("unchecked")
	public List<UmlDiagram> ripOutClassesFromModels() throws CoreException {
		List<String> configModels = configuration.getAttribute(MODEL, new ArrayList<String>());
		List<UmlDiagram> diagrams = new ArrayList<UmlDiagram>();

		for (String m : configModels) {
			IModel model = loadModelFromPropertiesString(m);
			StringProperties properties = new StringProperties();
			properties.load(m);
			String modelFilePath = properties.getProperty("modelFile");
			IFile modelIFile = ResourcesPlugin.getWorkspace().getRoot().getFile(new Path(modelFilePath));

			UmlDiagram diagram = new UmlDiagram(modelIFile);
			EolEvaluator eval = new EolEvaluator(model);

			// This here should really determine the type of model (class or state).
			// Initially I'm not going to worry about state diagrams and focus
			// on the class with the aim of refactoring this code at a later date, once
			// the algorithm has been formalised.

			// Grab the classes in the diagram
			Object classes =  eval.evaluate("Class.allOfType.collect(c|c)");

			if (classes instanceof Collection<?>){
				for (Object o : (Collection<?>)classes) {
					if (o instanceof Class) {
						diagram.addUmlClass(new UmlClass(((Class) o)));
					}
				}
			}

			// Get any associations in the diagram
			Object associations = eval.evaluate("Association.allOfType.collect(a|a)");
			
			if (associations instanceof Collection<?>) {
				for (Object a : (Collection<?>)associations) {
					if (a instanceof Association) {
						diagram.addUmlAssociation(new UmlAssociation(((Association) a)));
					}
				}
			}

			// the state diagram TODO
			// EolSequence stateSequence =
			// (EolSequence)eval.evaluate("StateMachine.allOfType.collect(c|c)");
			// //TODO
			diagrams.add(diagram);
		}
		return diagrams;
	}
}
